#include "parser.hpp"
#include <iostream>
using namespace std;
using namespace imitation::json;

Parser::Parser()
{
}

void Parser::load(const string &json)
{
    json_ = json;
    pos_ = 0;
}

void Parser::skip_whitespace()
{
    while(json_[pos_] == ' ' || json_[pos_] == '\n'|| json_[pos_] == '\t'|| json_[pos_] == '\r'){
        pos_++;
    }
}
char Parser::next()
{
    skip_whitespace();
    return json_[pos_++]; //+1?
}

Json imitation::json::Parser::parse_null()
{
    --pos_;
    if (json_.compare(pos_, 4, "null") == 0)
    {
        pos_ += 4;
        return Json();
    }
    cout << json_ <<' '<< pos_ << endl; //debug
    throw std::logic_error("expect null");
}
Json imitation::json::Parser::parse_bool()
{
    --pos_;
    if (json_.compare(pos_, 4, "true") == 0)
    {
        pos_ += 4;
        return Json(true);
    }
    else if (json_.compare(pos_, 5, "false") == 0)
    {
        pos_ += 5;
        return Json(false);
    }
    throw std::logic_error("expect bool");
}
Json imitation::json::Parser::parse_number()
{  
    int temp_pos = --pos_;
    char ch1 = json_[pos_];
    if(json_[pos_] == '-'){
        pos_++;
    }
    while(isdigit(json_[pos_])){
        pos_++;
    }
    if(json_[pos_] != '.'){
        int i = stoi(json_.substr(temp_pos, pos_ - temp_pos));
        return Json(i);
    }else{
        pos_++;
        while (isdigit(json_[pos_])){
            pos_++;
        }
        double d = stod(json_.substr(temp_pos, pos_ - temp_pos));
        return Json(d);
    }
    throw std::logic_error("expect number");
}
string imitation::json::Parser::parse_string()
{
    string result;
    char ch;
    while(true){
        ch = next();
        if(ch == '"'){
            break;
        }
        else if (ch == '\\')
        {
            ch = json_[++pos_];
            switch (ch)
            {
            case 'n': result += '\n';
                break;
            case 'r': result += '\r';
                break;
            case 't': result += '\t';
                break;
            case 'b': result += '\b';
                break;
            case 'f': result += '\f';
                break;
            case '"': result += "\\\"";
                break;
            case '\\': result += "\\\\";
                break;
            case 'u': result += json_.substr(pos_, 4);
                pos_ += 4;
                break;
            default:
                throw std::logic_error("invalid escape");
            }
        }else{
            result += ch;
        }
    }
    return result;
}
Json imitation::json::Parser::parse_array()
{
    Json arr(Json::json_array);
    char ch = next();
    if(ch == ']') return arr;
    pos_--;
    while(true){
        arr.append(parse());
        ch = next();
        if(ch == ']') return arr;
        if(ch != ','){
            throw std::logic_error("expect , or ]");
        }
    }
    return arr;
}
Json imitation::json::Parser::parse_object()
{
    Json obj(Json::json_object);
    char ch = next();
    if(ch == '}'){
        return obj;
    }
    pos_--;
    while(true){
        ch = next();
        if (ch != '"'){
            throw std::logic_error("expect key");
        }
        string key = parse_string();
        ch = next();
        if (ch != ':'){
            throw std::logic_error("expect :");
        }
        obj[key] = parse();
        ch = next();
        if(ch == '}') break;
        if(ch != ','){
            throw std::logic_error("expect , or }");
        }
    }
    return obj;
}

Json imitation::json::Parser::parse()   //输入string输出Json对象
{
    char ch = next();
    switch (ch)
    {
    case 'n':
        return parse_null();
    case 't':
    case 'f':
        return parse_bool();
    case '-':
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
        return parse_number();
    case '"':
        return Json(parse_string());
    case '[':
        return parse_array();
        break;
    case '{':
        return parse_object();
    default:
        throw std::logic_error("invalid json");
    }
}
